package org.marsdonne.commons;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
public class ResourceServerConfigurer extends WebSecurityConfigurerAdapter {
    private static final String PatternSeparator = " ";

    @Configuration
    @ConfigurationProperties("auth.authentication")
    public static class PermitConfigurer {
        private List<String> permitPatterns;

        public List<String> getPermitPatterns() {
            return permitPatterns;
        }

        public void setPermitPatterns(List<String> permitPatterns) {
            this.permitPatterns = permitPatterns;
        }
    }

    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    private String jwkSetUri;

    @Resource
    private PermitConfigurer permitConfigurer;

    @Bean
    @ConditionalOnMissingBean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowCredentials(false);
        configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "PUT", "OPTIONS", "POST"));
        configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers", "Content-Type", "Accept", "Authorization", "X-Requested-With", "Origin", "Accept"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable().csrf().disable()
                .authorizeRequests((requests) -> {
                            if (permitConfigurer.permitPatterns != null) {
                                for (String pattern : permitConfigurer.permitPatterns) {
                                    if (pattern.contains(PatternSeparator)) {
                                        String[] matcher = pattern.split(PatternSeparator);
                                        requests.antMatchers(HttpMethod.valueOf(matcher[0]), matcher[1]).permitAll();
                                    } else {
                                        requests.antMatchers(pattern).permitAll();
                                    }
                                }
                            }
                            requests.anyRequest().authenticated();
                        }
                ).oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
    }
}

